package com.alb.core.netty.processor;

import com.alb.common.enums.ResponseCode;
import com.alb.common.exception.BaseException;
import com.alb.common.exception.ConnectException;
import com.alb.common.exception.ResponseException;
import com.alb.core.ConfigLoader;
import com.alb.core.context.GatewayContext;
import com.alb.core.context.HttpRequestWrapper;
import com.alb.core.helper.AsyncHttpHelper;
import com.alb.core.helper.RequestHelper;
import com.alb.core.helper.ResponseHelper;
import com.alb.core.response.GatewayResponse;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import org.asynchttpclient.Request;
import org.asynchttpclient.Response;

import java.lang.ref.Reference;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeoutException;

/**
 * 最核心处理器，包含路由功能
 * 实现过程：
 * 1. 定义接口
 * 2. 最小可使用版本实现
 * 3. 路由函数实现
 * 4. 获取异步配置
 * 5. 异常处理
 * 6. 写回相应信息并释放资源
 * @author MattS
 * @date 2024/04/27
 */
@Slf4j
public class NettyCoreProcessor implements NettyProcessor {
    @Override
    public void process(HttpRequestWrapper wrapper) {
        //获取参数
        FullHttpRequest request = wrapper.getRequest();
        ChannelHandlerContext ctx = wrapper.getCtx();

        try {
            GatewayContext gatewayContext = RequestHelper.doContext(request, ctx);
            //路由
            route(gatewayContext);
        } catch (BaseException e) {
            log.error("process error {} {}",e.getCode().getCode(),e.getMessage());
            FullHttpResponse httpResponse = ResponseHelper.getHttpResponse(e.getCode());
            doWriteAndRelease(ctx,request,httpResponse);
        }catch (Throwable t){
            log.error("process unkown error",t);
            ResponseHelper.getHttpResponse(ResponseCode.INTERNAL_ERROR);
        }
    }

    /**
     * 回写数据，释放资源
     * @param ctx
     * @param request
     * @param httpResponse
     */
    private void doWriteAndRelease(ChannelHandlerContext ctx, FullHttpRequest request, FullHttpResponse httpResponse) {
        ctx.writeAndFlush(httpResponse).addListener(ChannelFutureListener.CLOSE);
        ReferenceCountUtil.release(request);
    }

    /**
     * 路由
     *
     * @param gatewayContext
     */
    private void route(GatewayContext gatewayContext) {
        //获取请求
        Request request = gatewayContext.getRequest().build();
        CompletableFuture<Response> future = AsyncHttpHelper.getInstance().executeRequest(request);

        //看看是否是单异步模式
        boolean whenComplete = ConfigLoader.getConfig().isWhenComplete();

        if (whenComplete) {
            //单异步模式
            future.whenComplete((response, throwable) -> {
                complete(request, response, throwable, gatewayContext);
            });
        } else {
            //双异步模式
            future.whenCompleteAsync(((response, throwable) -> {
                complete(request, response, throwable, gatewayContext);
            }));
        }
    }

    private void complete(Request request,
                          Response response,
                          Throwable throwable,
                          GatewayContext gatewayContext) {
        gatewayContext.releaseRequest();

        //分析捕捉到的异常，并处理异常
        try {
            if (Objects.nonNull(throwable)) {
                String url = request.getUrl();
                if (throwable instanceof TimeoutException) {
                    log.warn("complete time out {}", url);
                    //上下文中记录异常
                    gatewayContext.setThrowable(new ResponseException(ResponseCode.REQUEST_TIMEOUT));
                } else {
                    gatewayContext.setThrowable(new ConnectException(
                            throwable,
                            gatewayContext.getUniqueId(),
                            url,
                            ResponseCode.HTTP_RESPONSE_ERROR
                    ));
                }
            } else {
                gatewayContext.setResponse(GatewayResponse.buildGatewayResponse(response));
            }
        } catch (Throwable t) {
            gatewayContext.setThrowable(new ResponseException(ResponseCode.INTERNAL_ERROR));
            log.error("complete error",t);
        }finally {
            gatewayContext.written();
            ResponseHelper.writeResponse(gatewayContext);
        }
    }
}
